package com.xjrsoft.module.organization.controller;

import cn.dev33.satoken.secure.SaSecureUtil;
import cn.dev33.satoken.session.SaSession;
import cn.dev33.satoken.session.TokenSign;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.TypeReference;
import cn.hutool.core.util.StrUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.xjrsoft.common.annotation.XjrLog;
import com.xjrsoft.common.constant.GlobalConstant;
import com.xjrsoft.common.enums.EnabledMark;
import com.xjrsoft.common.exception.MyException;
import com.xjrsoft.common.handler.FormContentStyleStrategy;
import com.xjrsoft.common.model.result.R;
import com.xjrsoft.common.utils.*;
import com.xjrsoft.config.CommonPropertiesConfig;
import com.xjrsoft.module.organization.dto.*;
import com.xjrsoft.module.organization.entity.*;
import com.xjrsoft.module.organization.service.IUserService;
import com.xjrsoft.module.organization.vo.*;
import com.xjrsoft.module.oss.factory.OssFactory;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.validation.Valid;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.time.Duration;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;

/**
 * <p>
 * 用户 前端控制器
 * </p>
 *
 * @author tzx
 * @since 2022-03-02
 */
@RestController
@RequestMapping(GlobalConstant.ORGANIZATION_MODULE_PREFIX + "/user")
@Api(value = GlobalConstant.ORGANIZATION_MODULE_PREFIX + "/user", tags = "用户")
@AllArgsConstructor
public class UserController {

    private final IUserService userService;

    private final CommonPropertiesConfig propertiesConfig;

    private final RedisUtil redisUtil;


    @GetMapping(value = "/list")
    @ApiOperation(value = "用户列表（不分页）")
    public R list(String keyword) {
        String customSqlSegment = Wrappers.lambdaQuery(User.class)
                .like(StrUtil.isNotBlank(keyword), User::getUserName, keyword)
                .like(StrUtil.isNotBlank(keyword), User::getCode, keyword)
                .like(StrUtil.isNotBlank(keyword), User::getName, keyword)
                .like(StrUtil.isNotBlank(keyword), User::getMobile, keyword)
                .select(User.class, x -> VoToColumnUtil.fieldsToColumns(UserListVo.class).contains(x.getProperty())).getCustomSqlSegment();

        List<User> list = userService.list(Wrappers.lambdaQuery(User.class)
                .like(StrUtil.isNotBlank(keyword), User::getUserName, keyword)
                .like(StrUtil.isNotBlank(keyword), User::getCode, keyword)
                .like(StrUtil.isNotBlank(keyword), User::getName, keyword)
                .like(StrUtil.isNotBlank(keyword), User::getMobile, keyword)
                .select(User.class, x -> VoToColumnUtil.fieldsToColumns(UserListVo.class).contains(x.getProperty())));
        List<UserListVo> userListVos = BeanUtil.copyToList(list, UserListVo.class);
        return R.ok(userListVos);
    }

    @GetMapping(value = "/page")
    @ApiOperation(value = "用户列表（分页）")
    public R page(UserPageDto dto) {
        return R.ok(userService.page(dto));
    }

    @GetMapping(value = "/info")
    @ApiOperation(value = "根据id查询用户信息")
    public R info(@RequestParam Long id) {
        UserVo info = userService.getInfo(id);
        return R.ok(info);
    }


    @PostMapping
    @ApiOperation(value = "新增用户")
    public R add(@Valid @RequestBody AddUserDto dto) {
//        if (!OrganizationUtil.validatePassword(dto.getPassword())) {
//            return R.error("密码必须包含大写字母、小写字母、数字和特殊字符，长度8~16位");
//        }
        return R.ok(userService.add(dto));
    }

    @PutMapping
    @ApiOperation(value = "修改用户 不能修改用户名")
    public R update(@Valid @RequestBody UpdateUserDto dto) {
        return R.ok(userService.update(dto));
    }


    @GetMapping(value = "/current/info")
    @ApiOperation(value = "当前登录用户信息")
    public R info() {

        SaSession tokenSession = StpUtil.getTokenSession();
        User user = tokenSession.get(GlobalConstant.LOGIN_USER_INFO_KEY, new User());
        UserInfoVo currentInfo = userService.getCurrentInfo(user);
        return R.ok(currentInfo);
    }


    @PutMapping("/update/info")
    @ApiOperation(value = "登陆人修改自己得用户信息")
    public R updateInfo(@RequestBody @Valid UpdateInfoDto dto) {
        User updateUserInfo = BeanUtil.toBean(dto, User.class);
        updateUserInfo.setId(StpUtil.getLoginIdAsLong());

        CompletableFuture.runAsync(() -> {
            List<User> list = userService.list();
            redisUtil.set(GlobalConstant.USER_CACHE_KEY, list);
        });
        return R.ok(userService.updateById(updateUserInfo));
    }


    @PutMapping("/update/password")
    @ApiOperation(value = "当前登录用户修改本人密码")
    public R updatePassword(@RequestBody @Valid UpdatePasswordDto dto) {
//        if (!OrganizationUtil.validatePassword(dto.getNewPassword())) {
//            return R.error("密码必须包含大写字母、小写字母、数字和特殊字符，长度8~16位");
//        }
        User user = userService.getById(StpUtil.getLoginIdAsLong());
        if (!StrUtil.equals(SaSecureUtil.md5BySalt(dto.getOldPassword(), GlobalConstant.SECRET_KEY), user.getPassword())) {
            return R.error("当前密码填写错误！");
        }
        if (!StrUtil.equals(dto.getNewPassword(), dto.getConfirmPassword())) {
            return R.error("2次密码输入不一致！");
        }

        User updateUser = new User();
        updateUser.setId(StpUtil.getLoginIdAsLong());
        updateUser.setPassword(SaSecureUtil.md5BySalt(dto.getNewPassword(), GlobalConstant.SECRET_KEY));

        return R.ok(userService.updateById(updateUser));
    }

    @PostMapping("/update/avatar")
    @ApiOperation(value = "当前登录用户修改头像")
    public R uploadAvatar(@RequestParam("file") MultipartFile file) throws Exception {
        if (file.isEmpty()) {
            throw new MyException("上传文件不能为空");
        }

        //上传文件
        String suffix = Objects.requireNonNull(file.getOriginalFilename()).substring(file.getOriginalFilename().lastIndexOf(StringPool.DOT));
        String url = Objects.requireNonNull(OssFactory.build()).uploadSuffix(file.getBytes(), suffix);

        User updateUser = new User();
        updateUser.setId(StpUtil.getLoginIdAsLong());
        updateUser.setAvatar(url);
        userService.updateById(updateUser);

        SaSession tokenSession = StpUtil.getTokenSession();
        User user = tokenSession.get(GlobalConstant.LOGIN_USER_INFO_KEY, new User());

        user.setAvatar(url);
        tokenSession.set(GlobalConstant.LOGIN_USER_INFO_KEY, user);


        return R.ok(url);
    }

    @DeleteMapping
    @ApiOperation(value = "删除用户(可批量)")
    public R delete(@RequestBody List<Long> ids) {
        if (ids.contains(GlobalConstant.SUPER_ADMIN_USER_ID)) {
            R.error("管理员账户不能删除！");
        }

        if (ids.contains(StpUtil.getLoginIdAsLong())) {
            R.error("当前登录账户不能删除！");
        }
        //删除时需要同时删除用户部门关联表和用户角色关联表和用户岗位关系表数据。
        return R.ok(userService.deleteBatch(ids));
    }

    @PostMapping("/info/multi")
    @ApiOperation(value = "批量获取用户信息")
    public R usersInfo(@RequestBody List<Long> ids) {
        return R.ok(userService.getUsersInfo(ids));
    }

    @GetMapping("/enabled")
    @ApiOperation(value = "启用/禁用用户")
    public R enabled(@RequestParam Long id) {
        User user = userService.getOne(Wrappers.<User>query().lambda().select(User::getEnabledMark).eq(User::getId, id), false);
        if (user != null) {
            User updateUser = new User();
            updateUser.setId(id);
            updateUser.setEnabledMark(user.getEnabledMark() == EnabledMark.ENABLED.getCode() ? EnabledMark.DISABLED.getCode() : EnabledMark.ENABLED.getCode());
            return R.ok(userService.updateById(updateUser));
        }
        CompletableFuture.runAsync(() -> {
            List<User> list = userService.list();
            redisUtil.set(GlobalConstant.USER_CACHE_KEY, list);
        });
        return R.error("该用户不存在！");
    }

    @PutMapping("/reset-password")
    @ApiOperation(value = "重置密码")
    public R resetPassword(@RequestBody ResetPasswordDto dto) {
        User user = new User();
        user.setId(dto.getId());
//        user.setPassword(DigestUtil.md5Hex(propertiesConfig.getDefaultPassword()));
        user.setPassword(SaSecureUtil.md5BySalt(propertiesConfig.getDefaultPassword(), GlobalConstant.SECRET_KEY));
        CompletableFuture.runAsync(() -> {
            List<User> list = userService.list();
            redisUtil.set(GlobalConstant.USER_CACHE_KEY, list);
        });
        return R.ok(userService.updateById(user));
    }

    @GetMapping("/online-users/page")
    @ApiOperation(value = "获取在线人员")
    public R onlineUsersPage(OnlineUsersPageDto dto) {
        List<String> sessionIdList = StpUtil.searchSessionId(StringPool.EMPTY, 0, Integer.MAX_VALUE, false);
        List<OnlineUsersPageVo> loginUserList = new ArrayList<>(sessionIdList.size());
        LocalDateTime currentTime = LocalDateTime.now();
        for (String sessionId : sessionIdList) {
            SaSession session = StpUtil.getSessionBySessionId(sessionId);
            List<TokenSign> tokenSignList = session.getTokenSignList();
//            // 根据设备过滤，多个登录token，找出最近登录的
//            Optional<TokenSign> tokenSignOpt = tokenSignList.stream().filter(ts -> StrUtil.isEmpty(dto.getDevice()) || StrUtil.equalsIgnoreCase(dto.getDevice(), ts.getDevice()))
//                    .max(((o1, o2) -> {
//                        SaSession tokenSession1 = StpUtil.getTokenSessionByToken(o1.getValue());
//                        SaSession tokenSession2 = StpUtil.getTokenSessionByToken(o2.getValue());
//                        return Long.compare(tokenSession1.getCreateTime(), tokenSession2.getCreateTime());
//                    }));
            for (TokenSign tokenSign : tokenSignList) {
//                TokenSign tokenSign = tokenSignOpt.get();
                SaSession tokenSession = StpUtil.getTokenSessionByToken(tokenSign.getValue());
                User user = tokenSession.get(GlobalConstant.LOGIN_USER_INFO_KEY, new User());
                // 根据设备以及模糊查询条件过滤
                if ( (StrUtil.isNotEmpty(dto.getDevice()) && !StrUtil.equalsIgnoreCase(dto.getDevice(), tokenSign.getDevice()))
                        || (StrUtil.isNotEmpty(dto.getKeyword())
                            && !StrUtil.containsAnyIgnoreCase(user.getName(), dto.getKeyword())
                            && !StrUtil.containsAnyIgnoreCase(user.getUserName(), dto.getKeyword()))) {
                    continue;
                }
                OnlineUsersPageVo onlineUsersPageVo = new OnlineUsersPageVo();
                onlineUsersPageVo.setId(user.getId());
                onlineUsersPageVo.setName(user.getName());
                onlineUsersPageVo.setUserName(user.getUserName());
                // 在线时长计算
                LocalDateTime loginTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(tokenSession.getCreateTime()), ZoneId.systemDefault());
                onlineUsersPageVo.setLoginTime(loginTime);
                Duration duration = Duration.between(loginTime, currentTime);
                onlineUsersPageVo.setOnlineTime(LocalDateTimeUtil.tranDurationToShow(duration));
                // 部门名称组装
                List<Department> departments = tokenSession.get(GlobalConstant.LOGIN_USER_DEPT_LIST_KEY, new ArrayList<Department>(0));
                if (CollectionUtils.isNotEmpty(departments)) {
                    onlineUsersPageVo.setDepartmentNames(departments.stream().map(Department::getName).collect(Collectors.joining(StringPool.COMMA)));
                }
                loginUserList.add(onlineUsersPageVo);
            }
        }
        List<OnlineUsersPageVo> sortedList = loginUserList.stream().sorted(((o1, o2) -> o2.getLoginTime().compareTo(o1.getLoginTime()))).collect(Collectors.toList());
        return R.ok(ListUtils.Pager(dto.getSize(), dto.getLimit(), sortedList));
    }

    //踢人下线功能演示环境注释,如果需要自己放开
//    @PostMapping("/offline-users")
//    @ApiOperation(value = "强制下线在线人员")
//    public R onlineUsersPage(@RequestBody OfflineUsersDto dto) {
//        for (Long userId : dto.getUserIds()) {
//            StpUtil.logout(userId, dto.getDevice());
//        }
//        return R.ok();
//    }

    @GetMapping("/get-user-organization-info")
    @ApiOperation(value = "获取当前人员的组织架构信息")
    public R getUserOrganizationInfo() {
        //从缓存中获取当前登陆人的所有组织架构信息
        List<Long> roleIdList = StpUtil.getTokenSession().get(GlobalConstant.LOGIN_USER_ROLE_ID_KEY, new ArrayList<>());

        List<Post> postList = StpUtil.getTokenSession().get(GlobalConstant.LOGIN_USER_POST_LIST_KEY, new ArrayList<>());

        List<Department> departmentList = StpUtil.getTokenSession().get(GlobalConstant.LOGIN_USER_DEPT_LIST_KEY, new ArrayList<>());

        UserOrganizationInfoVo userOrganizationInfoVo = new UserOrganizationInfoVo();

        if (roleIdList.size() > 0){
            //获取所有的角色信息
            List<Role> allRoleList = redisUtil.get(GlobalConstant.ROLE_CACHE_KEY, new TypeReference<List<Role>>() {
            });
            //获取当前登录人的角色名称集合
            List<String> roleNameList = allRoleList.stream().filter(x->roleIdList.contains(x.getId())).map(Role::getName).collect(Collectors.toList());
            userOrganizationInfoVo.setRoleNameList(roleNameList);
        }

        if (postList.size() > 0){
            //获取当前登陆人的岗位名称集合
            List<String> postNameList = postList.stream().map(Post::getName).collect(Collectors.toList());
            userOrganizationInfoVo.setPostNameList(postNameList);
        }

        if (departmentList.size() > 0){
            //所有的部门信息
            List<Department> allDepartmentList = redisUtil.get(GlobalConstant.DEP_CACHE_KEY, new TypeReference<List<Department>>() {
            });
            List<String> departmentNameList = new ArrayList<>(departmentList.size());
            for (Department department : departmentList) {
                //获取到当前部门的层级
                String hierarchy = department.getHierarchy();
                if (StrUtil.isNotBlank(hierarchy)){
                    //对层级进行分割
                    String[] split = hierarchy.split(StringPool.DASH);

                    String departmentName = StringPool.EMPTY;
                    //获取对应层级的部门名称
                    if (split.length > 0){
                        for (int i = 0;i < split.length ;i++){
                            String s = split[i];
                            Department hierarchyDepartment = allDepartmentList.stream().filter(x->x.getId().toString().equals(s)).findFirst().orElse(new Department());
                            if (hierarchyDepartment != null && StrUtil.isNotBlank(hierarchyDepartment.getName())){
                                if (StrUtil.isNotBlank(departmentName)){
                                    departmentName = departmentName + StringPool.DASH + hierarchyDepartment.getName();
                                }else {
                                    departmentName = hierarchyDepartment.getName();
                                }
                            }
                        }
                    }
                    //构建所属部门的层级名称
                    departmentNameList.add(departmentName);
                }
            }
            userOrganizationInfoVo.setDepartmentNameList(departmentNameList);
        }
        return R.ok(userOrganizationInfoVo);
    }

    @PostMapping(value = "/export")
    @ApiOperation(value = "导出")
    @XjrLog(value = "导出表单数据")
    public ResponseEntity<byte[]> export(@Valid @RequestBody Long deptId) {
        List<User> list = userService.list(Wrappers.<User>lambdaQuery().in(User::getDepartmentId, deptId));
        List<UserExportVo> userExportVos = new ArrayList<>(list.size());
        List<UserDeptRelation> userDeptRelations = redisUtil.get(GlobalConstant.USER_DEPT_RELATION_CACHE_KEY, new TypeReference<List<UserDeptRelation>>() {
        });
        for (User user : list) {
            UserExportVo userExportVo = BeanUtil.toBean(user, UserExportVo.class);
            List<Long> deptIds = userDeptRelations.stream().filter(x -> x.getUserId().equals(user.getId())).map(UserDeptRelation::getDeptId).collect(Collectors.toList());
            if (CollectionUtils.isNotEmpty(deptIds)){
                userExportVo.setDepartmentIds(StrUtil.join(StringPool.COMMA,deptIds));
            }
            userExportVos.add(userExportVo);
        }
        ByteArrayOutputStream bot = new ByteArrayOutputStream();
        EasyExcel.write(bot).head(UserExportVo.class).automaticMergeHead(false)
                .registerWriteHandler(new FormContentStyleStrategy())
                .excelType(ExcelTypeEnum.XLSX).sheet().doWrite(userExportVos);
        ByteArrayOutputStream resultBot = ExcelUtil.renderExportRequiredHead(bot);
        return R.fileStream(resultBot.toByteArray(), "用户导出" + ExcelTypeEnum.XLSX.getValue());
    }

    @GetMapping("/download-template")
    @ApiOperation(value = "下载模板")
    @SneakyThrows
    public ResponseEntity<byte[]> downloadTemplate() {
        List<UserExportVo> databaselinkExportVoList = new ArrayList<>();
        ByteArrayOutputStream bot = new ByteArrayOutputStream();
        EasyExcel.write(bot).head(UserExportVo.class).automaticMergeHead(false)
                .registerWriteHandler(new FormContentStyleStrategy())
                .excelType(ExcelTypeEnum.XLSX).sheet().doWrite(databaselinkExportVoList);
        ByteArrayOutputStream resultBot = ExcelUtil.renderExportRequiredHead(bot);
        return R.fileStream(resultBot.toByteArray(), "用户导入模板" + ExcelTypeEnum.XLSX.getValue());
    }

    @PostMapping("/import")
    @ApiOperation(value = "导入")
    public R importData(@RequestParam MultipartFile file) throws IOException {
        return R.ok(userService.importData(file));
    }
}
